<?php
class DBSessionHandler extends SessionHandler {
	
	const TABLE_POSTFIX = "_session";
	
	protected $tableName;
	
	/**
	 * Constructor: by default attempts to create a table. In production should be instantiated with false.
	 * @param boolean $pCreateTable
	 */
	public function __construct($pCreateTable = true) {
		parent::__construct();
		$this->tableName = Conf::get('SESSION_NS') . self::TABLE_POSTFIX;		
		$this->setExceptionsLogger( DBSessionLogger::getInstance($this->tableName) );
		if ($pCreateTable == true) $this->createTable();
	}
	
	/**
	 * Creates a table, where session data will be stored.
	 */
	public function createTable() { 
		$sqlCheck = "SHOW TABLES";
		$sql = "CREATE TABLE IF NOT EXISTS `".$this->tableName."` (
  					`n_id` int(10) unsigned NOT NULL auto_increment,
  					`sess_id` varchar(255) NOT NULL,
  					`data_str` text NOT NULL,
  					`time_stamp` int(11) NOT NULL,
  						PRIMARY KEY  (`n_id`),
  						UNIQUE KEY `sess_id` (`sess_id`),
  						KEY `time_stamp` (`time_stamp`)
						) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;";
		try {
			$arr = DBAdapter::query($sqlCheck , DBAdapter::ASSOC);
			if (is_array($arr)) foreach($arr as $row) {
				if ($row['Tables_in_optax_db'] == $this->tableName) return;
			}
		 	DBAdapter::query($sql , DBAdapter::NONE);
		} 
		 catch (Exception $e) {
		 	  $this->handleException($e);
		 }
	}
	
	
	public function open( $save_path, $session_name ) {
		return true;
	}
	
	public function close() {
		return true;
	}
	
	public function read( $session_id ) {
		$sql = "SELECT data_str FROM ".$this->tableName." WHERE sess_id={:sess_id:}";
		$q = new QueryWrapper($sql);
		$q->sess_id = $session_id;
		
		try {
			$q->execute();
		}
		 catch(Exception $e) {
		 	 $this->handleException($e);
		 	 return '';			 	
		 }
		 
		if ($q->count() > 0) {
			$res = $q->getAssoc();
			return $res[0]['data_str'];
		}
		return '';		
	}
	
	public function write( $session_id, $session_data ) {
		$t = time();// to avoid calling time() function twice.
		$qCheck = new QueryWrapper("SELECT n_id FROM ".$this->tableName." WHERE sess_id={:sess_id:}");
		$qCheck->sess_id = $session_id;
		
		DBAdapter::query("START TRANSACTION", DBAdapter::NONE);
		$qCheck->execute();
		if (!is_null($qCheck->getAssoc())) {
			//prepare update query:
			$sql = "UPDATE ".$this->tableName." SET data_str={:data:}, time_stamp={:time_stamp:} WHERE sess_id={:sess_id:}";
		}
		 else {
		 	 //prepare insert query:
		 	 $sql = "INSERT INTO ".$this->tableName." (sess_id,data_str, time_stamp) VALUES ({:sess_id:}, {:data:}, {:time_stamp:})"; 
		 }
		
		$writeQ = new QueryWrapper($sql);
		$writeQ->sess_id    = $session_id;
		$writeQ->data    	  = $session_data;
		$writeQ->time_stamp = $t;
		
		try {
			$writeQ->execute();
		}
		 catch(Exception $e) {
		 	 DBAdapter::query("ROLLBACK", DBAdapter::NONE);
		 	 $this->handleException($e);
		 	 return false;			 	
		 }
		
		DBAdapter::query("COMMIT", DBAdapter::NONE); 
		return true;
	}
	
	
	
	public function destroy( $session_id ) {
		$sql = "DELETE FROM ".$this->tableName." WHERE sess_id={:sess_id:}";
		$q = new QueryWrapper($sql);
		$q->sess_id    = $session_id;
		try {
			$q->execute();
		}
		 catch(Exception $e) {
		 	 $this->handleException($e);
		 	 return false;			 	
		 }
		if ($q->affectedRows() > 0) return true;
		return false;		
	}
	
	public function gc( $max_expire_time ) {
		$oldestAllowed = time() - $max_expire_time;
		$sql = "DELETE FROM ".$this->tableName." WHERE time_stamp<{:oldest:} ";
		$q = new QueryWrapper($sql);
		$q->oldest 		 = $oldestAllowed; 
		
		try {
			$q->execute();
		}
		 catch(Exception $e) {
		 	 $this->handleException($e);
		 	 return false;			 	
		 }
		
		return $q->affectedRows();
	}
	
	public static function gcCleanUp() {
		$sh = new DBSessionHandler();
		$res = $sh->gc($sh->session_gc_maxlifetime);
		session_destroy();
		return $res;
	}
	
	/**
	 * Installs itself as a session handler
	 * @param boolean $pCreateTable
	 * @return boolean 
	 */
	public static function install($pCreateTable) {
		$sh = new DBSessionHandler($pCreateTable);
		return session_set_save_handler(  
 	  																array($sh,"open"),  
 	 																	array($sh,"close"),  
 	 																	array($sh,"read"),  
   		 	 													  array($sh,"write"),  
   		 	 													  array($sh,"destroy"),  
   		 	 														array($sh,"gc")
																	);
	}
	
}